// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"

//----------------------------------------------------------------------
// ExceptionHandler
// 	Entry point into the Nachos kernel.  Called when a user program
//	is executing, and either does a syscall, or generates an addressing
//	or arithmetic exception.
//
// 	For system calls, the following is the calling convention:
//
// 	system call code -- r2
//		arg1 -- r4
//		arg2 -- r5
//		arg3 -- r6
//		arg4 -- r7
//
//	The result of the system call, if any, must be put back into r2. 
//
// And don't forget to increment the pc before returning. (Or else you'll
// loop making the same system call forever!
//
//	"which" is the kind of exception.  The list of possible exceptions 
//	are in machine.h.
//----------------------------------------------------------------------

void getStrfromReg(int addr, char *str) {
    int length = 0;
    int val, i;
    do {
        machine->ReadMem(addr++, 1, &val);
        length++;
    } while (val != '\0' && length < 256);
    addr -= length;
    for (i = 0; i < length; i++) {
        machine->ReadMem(addr+i, 1, &val);
        str[i] = (char)val;
    }
    str[i] = '\0';
}

void
SetupRegister(int which)
{
    currentThread->space->RestoreState();
    currentThread->RestoreUserState();
    machine->AdvancePC();
    machine->Run();
}

void
ExceptionHandler(ExceptionType which)
{
    int type = machine->ReadRegister(2);

    if ((which == SyscallException) && (type == SC_Halt)) {
	DEBUG('a', "Shutdown, initiated by user program.\n");
   	interrupt->Halt();
    } else if ((which == PageFaultException)) {
        int addr = machine->ReadRegister(BadVAddrReg);
    #ifdef USE_TLB
        machine->TLBSwap(addr);
    #endif
    } else if ((which == SyscallException) && (type == SC_Create)) {
        char filename[256] = "\0";
        getStrfromReg(machine->ReadRegister(4), filename);
        fileSystem->Create((char*)filename, 256);
        machine->AdvancePC();
    } else if ((which == SyscallException) && (type == SC_Open)) {
        char filename[256] = "\0";
        getStrfromReg(machine->ReadRegister(4), filename);
        OpenFile *openfile = fileSystem->Open((char*)filename);
    #ifdef FILESYS_STUB
        if (openfile != NULL) machine->WriteRegister(2, openfile->getfid());
        else machine->WriteRegister(2, -1);
    #endif  
        machine->AdvancePC();  
    } else if ((which == SyscallException) && (type == SC_Close)) {
        int fid = machine->ReadRegister(4);
        Close(fid);
        machine->AdvancePC();
    } else if ((which == SyscallException) && (type == SC_Write)) {
        char content[256] = "\0";
        getStrfromReg(machine->ReadRegister(4), content);
        int fid = machine->ReadRegister(6);
        if (fid < 0) {
            printf("Write : ");
            for (int i = 0; i < machine->ReadRegister(5); i++)
                printf("%c", content[i]);
            printf("\n");
        } else {
            OpenFile *openfile = new OpenFile(fid);
            openfile->Write(content, machine->ReadRegister(5));
            delete openfile;
        }
        machine->AdvancePC();
    } else if ((which == SyscallException) && (type == SC_Read)) {
        OpenFile *openfile = new OpenFile(machine->ReadRegister(6));
        char *content = new char[machine->ReadRegister(5)];
        int ret = openfile->Read(content, machine->ReadRegister(5));
        for (int i = 0; i < machine->ReadRegister(5); i++) {
            machine->WriteMem(machine->ReadRegister(4), 1, content[i]);
        }
        machine->WriteRegister(2, ret);
        delete openfile;
        machine->AdvancePC();
    } else if ((which == SyscallException) && (type == SC_Exec)) {
        char filename[256] = "\0";
        getStrfromReg(machine->ReadRegister(4), filename);
        OpenFile *executable = fileSystem->Open((char*)filename);
        AddrSpace *space;
        space = new AddrSpace(executable);    
        currentThread->space = space;
        delete executable;			
        space->InitRegisters();		
        space->RestoreState();		
        machine->Run();			
        ASSERT(FALSE);
    } else if ((which == SyscallException) && (type == SC_Exit)) {
        int exitcode = machine->ReadRegister(4);
        currentThread->Finish();
        machine->AdvancePC();
    } else if ((which == SyscallException) && (type == SC_Fork)) {
        Thread *newthread = new Thread("fork thread");
        newthread->space = currentThread->space;
        for (int i = 0; i < NumTotalRegs; i++)
	    newthread->userRegisters[i] = machine->ReadRegister(i);
        newthread->userRegisters[2] = 0;
        newthread->Fork(SetupRegister, 0);
        machine->WriteRegister(2, newthread->getThreadid());
        machine->AdvancePC();
    } else if ((which == SyscallException) && (type == SC_Yield)) {
        currentThread->Yield();
        machine->AdvancePC();
    } /*else if ((which == SyscallException) && (type == SC_Join)) {
        currentThread->Join();
	machine->AdvancePC();
    }*/
    else {
	printf("Unexpected user mode exception %d %d\n", which, type);
	ASSERT(FALSE);
    }
}
